祝大家中秋節快樂!
果然還是要跨過中秋連假才有參加鐵人賽的感覺啊哈哈哈哈
今天要為前端建立基本的架構,而重點就是要讓其具有 Reactivity,也就是當資料變化時,視覺介面會自動更新的特性。
這項特性可以讓開發者更輕鬆地建立動態的網頁,並提供使用者更好的體驗。
Ferris 也在遠端發送 Signal 親自指導:
🏮 今天完整的程式碼可以拉到最底下 Put it together 區塊或是在 GitHub 找到。
現在的 app.rs
中有很多自動生成的程式碼是我們不需要的,這裡要把它們先刪掉,由上到下分別是:
// content for this welcome page
底下 <Router>
到 </Router>
之間的程式碼都刪掉。/// Renders the home page of your application.
下方的 component 整個刪掉。/// 404 - Not Found
下方的 component 也刪掉。而我們的前端只有三個主要部件,
所以這裡只需要在 App
component 的下方加上我們需要的部件就好,請把下面的程式碼加到 <Title text="Iron LLaMa"/>
下面:
// basic layout
<MessageHistory/>
<MessageInputField/>
作為前端的兩個主要區塊。
建立好架構之後,就可以為頁面的主要部件來實作一些邏輯了。
首先要為對話建立一個 signal,程式碼請放在 app.rs
的 App
component 裡面(provide_meta_context();
下面):
let (conversaton, set_conversaton) = create_signal(Conversation::new());
正如 [Day 12] - 鋼鐵草泥馬 🦙 LLM chatbot 🤖|Leptos 小教室 所介紹的 create_signal 會回傳一個 tuple。
其中第一個元素的型別為 ReadSignal
,第二個為 WriteSignal
,分別對應到讀寫操作
若我們想要,可以透過 conversaton.get()
(或 conversation()
) 將訊息內容印在 view! 模板中。
而從這個 view 回傳的頁面,會在有人更新這個 signal 時自動重新渲染,這就是 Leptos 中 Reactivity 的體現。
因為有用到
Conversation
所以記得在檔案上方引入use crate::model::conversation::Conversation;
接著要建立所謂的 action
,因為這個 App 基本上都要靠使用者輸入問題並按下送出才會開始一系列的動作:
Conversation
中,讓我們可以在歷史訊息的區塊中看見它user: false
),要再把它加入 Conversation
中,讓我們可以在歷史訊息的區塊中看見它而在 leptos 中 action 就是能處理這個流程的工具。
這裡我們把這個 action 取名為 send
:
let send = create_action(move |new_message: &String| {
let user_message = Message {
text: new_message.clone(),
user: true,
};
set_conversation.update(move |c| c.messages.push(user_message));
async move { todo!("converse") }
});
其中 create_action
的引數是一個閉包,而這個閉包的輸入值就是要給 Signal 的輸入值,我們在這裡用它建立一個新的訊息 user_message
。
而後面就可以透過 set_conversation
把新建立的 Message
更新上去,這裡 .update
方法也是以一個閉包做為引數,這個閉包的輸入值則是 Signal 當前的數值。
而最後的 todo!
則留待後面再實作,它的功能基本上就是把對話送到伺服器端,然後等到伺服器端回傳結果後再進行處理。
最後,雖然還沒有實作 <MessageHistory/>
與 <MessageInputField/>
component,但前面的 action 要給輸入區塊 <MessageInputField send/>
,這樣在使用者按下送出鈕後,才會啟動一連串的動作。
而 conversation 則要給 <MessageHistory conversation/>
,這樣這個區塊才會在每次 Conversation 更新時自動重新渲染。
所以今天最後 app.rs
會長這樣:
use leptos::*;
use leptos_meta::*;
use crate::model::conversation::{Conversation, Message};
#[component]
pub fn App() -> impl IntoView {
// Provides context that manages stylesheets, titles, meta tags, etc.
provide_meta_context();
let (conversation, set_conversation) = create_signal(Conversation::new());
let send = create_action(move |new_message: &String| {
let user_message = Message {
text: new_message.clone(),
user: true,
};
set_conversation.update(move |c| c.messages.push(user_message));
async move { todo!("converse") }
});
view! {
// injects a stylesheet into the document <head>
// id=leptos means cargo-leptos will hot-reload this stylesheet
<Stylesheet id="leptos" href="/pkg/iron_llama.css"/>
// sets the document title
<Title text="Iron LLaMa"/>
<MessageHistory conversation/>
<MessageInputField send/>
}
}
好啦,明天要開始建立後端的邏輯了,明天見囉~
寫到這邊才發現今天都沒有迷因,但還是補一張好了
雖然一點意義都沒有哈哈哈